import { _decorator, Component, sys, Asset, director, Game } from 'cc';
import { EventSystem } from './EventSystem';
import { BaseModule } from './base/BaseModule';
import { GameFramework } from './base/GameFramework';
import { GameEvent } from '../../game/configs/GameEvent';

const { ccclass } = _decorator;

export interface HotUpdateInfo {
    version: string;
    packageUrl: string;
    remoteManifestUrl: string;
    remoteVersionUrl: string;
}

@ccclass('HotUpdateSystem')
export class HotUpdateSystem extends BaseModule {
    init(): void {
        // 初始化热更新系统
        // 使用本地IIS服务器地址，需要替换为您的本地IP地址
        const manifestUrl = "http://localhost/hotupdate/project.manifest";
        this.init1(manifestUrl);
        
        // GameFramework.event.on(GameEvent.GAME_UPDATE, this.hotUpdate, this);
    }
    shutdown(): void {
        // GameFramework.event.off(GameEvent.GAME_UPDATE, this.hotUpdate, this);
    }

    private _am: any = null; // AssetsManager 引用
    private _updating: boolean = false;
    private _canRetry: boolean = false;
    private _storagePath: string = "";
    private _version: string = "1.0.0";
    private _checkListener: any = null;
    private _updateListener: any = null;
    
    /**
     * 需要热更新的目录
     */
    private _hotUpdateDirs: string[] = [
        "assets/CocosGameFramework/scripts/game",
        "assets/CocosGameFramework/resources"
    ];


    /**
     * 初始化热更新系统
     * @param manifestUrl manifest文件路径
     */
    init1(manifestUrl: string) {
        if (!sys.isNative) {
            console.warn("热更新仅在原生平台支持，在编辑器预览模式下将模拟热更新流程");
            // 模拟热更新初始化成功
            GameFramework.event.emit("HOTUPDATE_UPTODATE");
            return;
        }

        // 初始化热更新管理器
        this._storagePath = ((jsb as any).fileUtils ? (jsb as any).fileUtils.getWritablePath() : '/') + 'remote-asset';
        console.log('热更新存储路径: ' + this._storagePath);

        // 创建 AssetsManager
        this._am = new (jsb as any).AssetsManager(manifestUrl, this._storagePath, this.versionCompareHandle.bind(this));
        this._am.setVerifyCallback(this.verifyCallback.bind(this));

        if (sys.os === sys.OS.ANDROID) {
            this._am.setMaxConcurrentTask(2);
        }
        
        console.log("HotUpdateSystem initialized");
    }

    /**
     * 版本比较函数
     */
    private versionCompareHandle(versionA: string, versionB: string): number {
        console.log("版本比较: " + versionA + " vs " + versionB);
        const vA = versionA.split('.');
        const vB = versionB.split('.');
        
        for (let i = 0; i < Math.max(vA.length, vB.length); i++) {
            const a = parseInt(vA[i] || '0');
            const b = parseInt(vB[i] || '0');
            
            if (a !== b) {
                return a - b;
            }
        }
        
        return 0;
    }

    /**
     * 文件校验回调
     */
    private verifyCallback(path: string, asset: any): boolean {
        // 校验下载文件的完整性
        const compressed = asset.compressed;
        // 对于压缩的资源，校验其md5值
        if (compressed) {
            return true;
        } else {
            console.log("校验 " + path);
            return true;
        }
    }

    /**
     * 检查更新
     */
    checkUpdate() {
        if (!sys.isNative) {
            console.log("在非原生平台模拟检查更新");
            // 模拟发现新版本
            setTimeout(() => {
                GameFramework.event.emit("HOTUPDATE_NEWVERSION");
            }, 1000);
            return;
        }

        if (!this._am) {
            console.error("热更新管理器未初始化");
            return;
        }

        if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {
            console.error("本地 manifest 未找到或加载失败");
            return;
        }

        this._checkListener = this.checkCb.bind(this);
        this._am.setEventCallback(this._checkListener);
        this._am.checkUpdate();
    }

    /**
     * 检查更新回调
     */
    private checkCb(event: any) {
        console.log('检查更新回调');
        const code = event.getEventCode();
        
        switch (code) {
            case (jsb as any).EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                console.log("未找到本地 manifest 文件");
                GameFramework.event.emit("HOTUPDATE_ERROR", "ERROR_NO_LOCAL_MANIFEST");
                break;
            case (jsb as any).EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
                console.log("下载 manifest 文件失败");
                GameFramework.event.emit("HOTUPDATE_ERROR", "ERROR_DOWNLOAD_MANIFEST");
                break;
            case (jsb as any).EventAssetsManager.ERROR_PARSE_MANIFEST:
                console.log("解析 manifest 文件失败");
                GameFramework.event.emit("HOTUPDATE_ERROR", "ERROR_PARSE_MANIFEST");
                break;
            case (jsb as any).EventAssetsManager.ALREADY_UP_TO_DATE:
                console.log("已经是最新版本");
                GameFramework.event.emit("HOTUPDATE_UPTODATE");
                break;
            case (jsb as any).EventAssetsManager.NEW_VERSION_FOUND:
                console.log("发现新版本");
                GameFramework.event.emit("HOTUPDATE_NEWVERSION");
                break;
            default:
                return;
        }

        this._am.setEventCallback(null);
    }

    /**
     * 执行热更新
     */
    hotUpdate() {
        if (!sys.isNative) {
            console.log("在非原生平台模拟热更新过程");
            // 模拟热更新进度
            let progress = 0;
            const interval = setInterval(() => {
                progress += 0.1;
                if (progress <= 1.0) {
                    GameFramework.event.emit("HOTUPDATE_PROGRESS", {
                        percent: progress,
                        filePercent: progress,
                        message: "模拟更新中..."
                    });
                } else {
                    clearInterval(interval);
                    GameFramework.event.emit("HOTUPDATE_FINISHED");
                }
            }, 500);
            return;
        }

        if (!this._am) {
            console.error("热更新管理器未初始化");
            return;
        }

        if (!this._updating) {
            this._updateListener = this.updateCb.bind(this);
            this._am.setEventCallback(this._updateListener);

            if (this._am.getState() === (jsb as any).AssetsManager.State.UNINITED) {
                const url = this._am.getLocalManifest().getManifestFileUrl();
                this._am.loadLocalManifest(url);
            }
            
            // 设置只更新指定目录
            const manifest = this._am.getLocalManifest();
            if (manifest) {
                const searchPaths = manifest.getSearchPaths();
                searchPaths.push(...this._hotUpdateDirs);
                manifest.setSearchPaths(searchPaths);
            }

            this._am.update();
            this._updating = true;
        }
    }

    /**
     * 更新回调
     */
    private updateCb(event: any) {
        console.log('更新回调');
        const code = event.getEventCode();
        
        switch (code) {
            case (jsb as any).EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                console.log("未找到本地 manifest 文件");
                GameFramework.event.emit("HOTUPDATE_ERROR", "ERROR_NO_LOCAL_MANIFEST");
                break;
            case (jsb as any).EventAssetsManager.UPDATE_PROGRESSION:
                const percent = event.getPercent();
                const filePercent = event.getPercentByFile();
                const msg = event.getMessage();
                console.log(`更新进度: ${percent.toFixed(2)}% (${filePercent.toFixed(2)}%), ${msg}`);
                GameFramework.event.emit("HOTUPDATE_PROGRESS", {
                    percent: percent,
                    filePercent: filePercent,
                    message: msg
                });
                break;
            case (jsb as any).EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
                console.log("下载 manifest 文件失败");
                GameFramework.event.emit("HOTUPDATE_ERROR", "ERROR_DOWNLOAD_MANIFEST");
                break;
            case (jsb as any).EventAssetsManager.ERROR_PARSE_MANIFEST:
                console.log("解析 manifest 文件失败");
                GameFramework.event.emit("HOTUPDATE_ERROR", "ERROR_PARSE_MANIFEST");
                break;
            case (jsb as any).EventAssetsManager.ALREADY_UP_TO_DATE:
                console.log("已经是最新版本");
                GameFramework.event.emit("HOTUPDATE_UPTODATE");
                break;
            case (jsb as any).EventAssetsManager.UPDATE_FINISHED:
                console.log("更新完成 " + event.getMessage());
                GameFramework.event.emit("HOTUPDATE_FINISHED");
                this._updating = false;
                break;
            case (jsb as any).EventAssetsManager.UPDATE_FAILED:
                console.log("更新失败 " + event.getMessage());
                GameFramework.event.emit("HOTUPDATE_FAILED");
                this._updating = false;
                this._canRetry = true;
                break;
            case (jsb as any).EventAssetsManager.ERROR_UPDATING:
                console.log("资源更新错误: " + event.getAssetId() + ", " + event.getMessage());
                GameFramework.event.emit("HOTUPDATE_ERROR", "ERROR_UPDATING");
                break;
            case (jsb as any).EventAssetsManager.ERROR_DECOMPRESS:
                console.log("解压错误: " + event.getMessage());
                GameFramework.event.emit("HOTUPDATE_ERROR", "ERROR_DECOMPRESS");
                break;
            default:
                break;
        }

        if (code === (jsb as any).EventAssetsManager.UPDATE_FINISHED || 
            code === (jsb as any).EventAssetsManager.UPDATE_FAILED) {
            this._am.setEventCallback(null);
        }
    }

    /**
     * 重试更新
     */
    retry() {
        if (!this._updating && this._canRetry) {
            this._canRetry = false;
            this._am.downloadFailedAssets();
        }
    }

    /**
     * 获取当前版本
     */
    getVersion(): string {
        return this._version;
    }

    /**
     * 设置版本
     */
    setVersion(version: string) {
        this._version = version;
    }
    
    /**
     * 检查热更新系统是否可用
     */
    isAvailable(): boolean {
        // 在非原生平台上也返回 true，以便在编辑器中测试
        return sys.isNative ? !!this._am : true;
    }
}